home *** CD-ROM | disk | FTP | other *** search
-
-
- MODELER'S LITTLE HELPER
- Tom Davis
-
-
-
- INTRODUCTION
-
- The files here generate a library of 3D modelling commands. In
- addition, there is a sample program that illustrates many of the
- modelling commands in action.
-
- To see some samples of objects that are easily constructed using the
- minimod tools, run the program called example. The left mouse
- manipulates the object in a track-ball manner, and the right mouse
- brings up the pop-up menu to change models and materials.
-
- The idea is very simple. There are library routines that build many
- simple shapes -- platonic solids, cylinders, spheres, worms, donuts,
- ... Each routine generates a set of triangles or quadrilaterals at the
- lowest level and executes a call-back procedure for each one. You
- provide the call-back function, and it can do anything it wants with
- the data. The vertices and normals for each triangle or quadrilateral
- are provided to the call-back procedure.
-
- For example, suppose you want to draw a sphere approximated by
- triangles exactly once, and never use the data again. Then the
- call-back function would take each triangle returned and draw it using
- graphics library routines. More often, however, the data will be saved
- in some internal database or display list for rapid re-display. You
- could also make the call-back function dump the data into some sort of
- file, or whatever you want.
-
- In addition to the routines that generate simple shapes, the library
- also maintains a matrix transformation stack similar to that in the GL
- so that transformations can be applied to the generated solids. The
- transformation takes normal information into account, even when
- non-uniform scales or shearing is applied.
-
- The interfaces are not all consistent with each other, as each was
- invented as needed. Some have information about centers and radii
- since they were written before the general transformation routines
- were available. There's tons of stuff that could be added. I'm
- interested in contributions and suggestions.
-
- THE STRUCTURE OF THE CALL-BACK PROCEDURE
-
- The user-supplied call-back procedure must have 9 parameters. The
- first is a long, and indicates whether the following data is for a
- triangle or a quadrilateral. The next eight are pointers to arrays of
- 3 floats in the order n0, v0, n1, v1, n2, v2, n3, v3. The n's stand
- for normal vectors and the v's for vertices. If a triangle is passed
- back as indicated by the first parameter, the contents of n3 and v3 may
- be garbage. The first parameter to the call-back routine is either
- ADD_QUAD or ADD_TRI (defined in the file 3d.h).
-
- PRIMITIVE MODELING ROUTINES
-
- Primitive modelling routines (in all cases, the function savefunc is
- the call-back routine):
-
- void drawbox(float x0, float x1, float y0, float y1,
- float z0, float z1, void (*savefunc)());
-
- Makes a rectangular box from x0 to x1 in the x-direction, and so on.
-
- void quadsphere(long latsides, long longsides, void (*savefunc)());
-
- Approximates a sphere with quadrilaterals, using the parameters
- for the number of latitude and longitude lines.
-
- void trisphere(float *center, float radius, long depth, void (*savefunc)());
-
- Approximates a sphere from triangles with the given center and
- radius. Depth is a measure of the amount of subdivision done.
- A depth of one corresponds to an icosahedron, a depth of 2
- divides each triangle into 4 pieces, depth 3 => 9 pieces, and
- depth n => n^2 pieces, so for a sphere at depth n, there will
- be 20*n^2 triangles. center is a pointer to the first of three
- floats.
-
- void doughnut(float r, float R, long nsides, long rings, void (*savefunc)());
-
- Makes a torus (doughnut) with a tube of radius r centered around
- a circle of radius R in the x-y plane. The circle is divided into
- rings divisions, and around each ring there are nsides sides.
-
- void elbow(float *p0, float *p1, float *p2,
- float radius, long nsides, long nsegs, void (*savefunc)());
-
- Makes a 90 degree elbow, centered at p0, and going from p1 to p2.
- The radius is given, as well as the number of segments and sides
- making up the elbow. p0, p1, and p2 are pointers to sets of 3
- floats.
-
- void cylinder(float *p1, float *p2,
- float radius, long nsides, void (*savefunc)());
-
- Makes a cylinder about the line segment connecting p1 to p2 having
- the given radius and number of sides. p1 and p2 are pointers to
- sets of 3 floats.
-
- void icosahedron(float *center, float r, void (*savefunc)());
- void octahedron(float *center, float r, void (*savefunc)());
- void tetrahedron(float *center, float r, void (*savefunc)());
- void dodecahedron(float *center, float r, void (*savefunc)());
-
- These routines make the corresponding platonic solids, centered
- at center, and having the given "radius". (I may have the
- radius wrong sometimes, but bigger numbers do make them bigger,
- and if the radii are about the same, the solids are about the
- same size.)
-
- CURVES: SMOOTH AND PIECEWISE LINEAR
-
- Some modelling routines use curves to construct objects. The
- constructed objects can be worms made by sweeping circles or other
- curves along curves, or solids of revolution. At present, there is no
- way to use the curve or piecewise linear curve information in any other
- way. The simplest curves are just piecewise linear curves, made by the
- routine:
-
- pwlin_t *makepwlin(long n, float *p);
-
- The number of (3-d) points along the curve is given by n, and p
- is a pointer to a list of 3*n floats. A pointer to a structure
- is returned that represents the piecewise linear curve.
-
- void freepwlin(pwlin *pw);
-
- This routine frees a piecewise linear curve generated by makepwlin().
-
- pwlin_t *appendpwlins(pwlin_t *pw1, pwlin_t *pw2);
-
- This routine appends two piecewise linear curves by attaching
- the end of pw1 to the beginning of pw2. The piecewise linear
- curve returned is different from pw1 and pw2, which are
- unaffected. If the final point of pw1 is equal to the initial
- point of pw2, the duplicate is eliminated; otherwise, the two
- are just connected by a line segment.
-
- Curves are saved as an analytic representation, and are basically
- converted to piecewise linear form internally whenever necessary. It
- probably would have been better to make curves and piecewise liner
- curves one object, but I didn't. There would have been a few ugly
- problems.
-
- curve_t *newcubiccurve(float *p1, float *p2, float *p3, float *p4);
- curve_t *newbeziercurve(float *p1, float *p2, float *p3, float *p4);
- curve_t *newbsplinecurve(float *p1, float *p2, float *p3, float *p4);
- curve_t *newcardinalcurve(float *p1, float *p2, float *p3, float *p4);
-
- These four routines return a pointer to a curve structure where
- the curves are all cubic splines based on the four given
- control points. The independent variable is assumed to run
- from 0 to 1.
-
- curve_t *newanalyticcurve(float (*x)(float), float (*y)(float), float (*z)(float),
- float (*dx)(float), float (*dy)(float), float (*dz)(float),
- float (*ddx)(float), float (*ddy)(float), float (*ddz)(float));
-
- An arbitrary analytic curve can be specified (for the
- independent variable running from 0.0 to 1.0. You must give
- functions for the x, y, and z coordinates as well as the
- analytic derivatives and second derivatives of the functions
- (with respect to the independent variable).
-
- pwlin_t *pwlinfromcurve(curve_t *c, long nsteps);
-
- This routine lets you make a piecewise linear curve from an
- analytic curve with the given number of steps. For now, it
- just takes nsteps uniform steps between 0.0 and 1.0.
-
- USING THE CURVE ROUTINES
-
- void makeworm(curve_t *c, float radius, long nsides,
- long nsegs, void (*savefunc)());
-
- Given a curve c, this routine generates a worm made by sweeping a
- circle along the curve and perpendicular to it. The circle has
- the given radius, and is approxiamted by a polygon with nsides.
- nsegs evenly spaced points along the curve are used.
-
- void makepwlworm(curve_t *c, pwlin_t *p, long nsteps, void (*savefunc)());
-
- If you want a shape other than a circle to be swept along the
- curve c, make an appropriate piecewise linear shape in the x-y
- plane, and pass it to this routine as the second parameter. The
- piecewise linear curve will be placed nsteps times along the
- curve and perpendicular to it, and corresponding points will be
- connected. The resulting worm will look faceted -- i.e. the
- normals will the perpendicular to the faces.
-
- void makesmoothpwlworm(curve_t *c, pwlin_t *p, long nsteps, void (*savefunc)());
-
- The same as the routine above, except that the normals at each
- facet intersection will be averaged to give a smooth worm.
-
- void solidofrevolution(pwlin_t *p, long nsteps, void (*savefunc)());
- void smoothsolidofrevolution(pwlin_t *p, long nsteps, void (*savefunc)());
-
- Both these routines sweep a piecewise linear curve about the y-axis
- and form a solid of revolution with nsteps evenly spaced steps
- taken around the y-axis. The smooth version averages the facet's
- normals; the regular version gives a faceted solid.
-
- ANALYTIC SURFACES
-
- If you have a parametric surface defined in terms of:
-
- (x(s,t), y(s,t), z(s,t))
-
- and you can take the analytic partial derivatives with respect to s and t:
-
- (xs(s,t), ys(s,t), zs(s,t))
- (xt(s,t), yt(s,t), zt(s,t))
-
- then to generate a surface for smin<s<smax and tmin<t<tmax, use the
- function:
-
- void analyticsurface(float (*x)(float, float), float (*y)(float, float),
- float (*z)(float, float),
- float (*xs)(float, float), float (*ys)(float, float),
- float (*zs)(float, float),
- float (*xt)(float, float), float (*yt)(float, float),
- float (*zt)(float, float),
- long scount, float smin, float smax,
- long tcount, float tmin, float tmax,
- long flipnormals,
- void (*savefunc)());
-
- x, y, z, xs, ys, zs, xt, yt, and zt are pointers to the functions
- described above; scount is the number of steps to take between smin and
- smax; tcount is the same thing in the t-direction, and flipnormals is
- either 1 or 0, and if it's 1, all the normal vector directions are
- flipped before being passed to savefunc(). savefunc() is as in any of
- the other examples.
-
- TRANSFORMATIONS
-
- The transformations are basically the same as those provided in the GL,
- except that there is just a single matrix stack, there is no general
- loadmatrix, multmatrix or getmatrix commands. The only unusual call is
- the shear() routine that multiplies by a shearing matrix that looks like
- this:
-
- | 1.0 xy xz 0.0 |
- | 0.0 1.0 yz 0.0 |
- | 0.0 0.0 1.0 0.0 |
- | 0.0 0.0 0.0 1.0 |
-
- void m_resetmatrixstack();
- void m_pushmatrix();
- void m_popmatrix();
- void m_shear(float xy, float xz, float yz);
- void m_translate(float tx, float ty, float tz);
- void m_scale(float sx, float sy, float sz);
- void m_rotate(float angle, char axis);
-
- m_resetmatrixstack() empties the stack and loads on an identity
- matrix. The m_rotate angle takes its angle parameter in radians.
- (If your angle is in degrees, multiply by PI/180.0 to convert
- to radians). The character can be 'x', 'X', 'y', 'Y', 'z', or
- 'Z' (for the rotate command).
-
- ERRORS
-
- If the library encounters an error, it will print out an error string
- and exit. If you don't like this behavior, you can replace the error
- handler by one of your own using the routine:
-
- void seterrorfunc(void (*func)(char *));
-
- Your routine func() will then be called with the same string that the
- default error handler would have spit out before it died. (Yeah, I
- know it's primitive.) Call the routine with 0 to restore the default
- behavior.
-
- ADDITIONAL USEFUL ROUTINES
-
- The library has a whole set of routines for handling 3-d vectors that
- are used extensively internally, and you may find them useful. They are
- listed below, and they "do the obvious thing". All the routines work
- fine if the output vector is the same as one of the input vectors.
-
- void diff3(float *v1, float *v2, float *v1minusv2);
- void add3(float *v1, float *v2, float *v1plusv2);
- void scalarmult(float s, float *v1, float *sv1);
- float dot3(float *v1, float *v2);
- float length3(float *v1);
- float dist3(float *v1, float *v2);
- void copy3(float *v1, float *copyofv1);
- void crossprod(float *v1, float *v2, float *v1crossv2);
- void normalize(float *v1);
- void print3(float *v);
- void printmat3(float m[3][3]);
- void identifymat3(float m[3][3]);
- void copymat3(float m[3][3], float newm[3][3]);
-
- USING THE LIBRARY
-
- A pretty good example is given in the program example.c. Most of the
- routines are used there. The worst thing about example.c is probably
- the display list used. I just have a giant array for triangle data
- and for quad data and dump stuff into it. To draw it, I just traverse
- the data. There's no checking for out of space, etc.
-
- Just be sure to include the file "3d.h", and everything should be cool.
-
- The example program uses a simple viewer (similar to the one in Showcase)
- that moves the model with a trackball-type interface controlled by the
- left mouse button. If you click down in the green area and move the
- mouse with the button down, the trackball follows. If you click outside
- the green circle, it's as if you grabbed it right on the edge. This makes
- it easy to do pure rotations about the z-axis (the axis pointing straight
- into the screen). The trackball interface is not great, but may be useful
- to somebody. It needs work. I'm hoping to use the Scenario viewers.
-
- ADDING YOUR OWN MODELING ROUTINES
-
- This isn't very hard to do -- look at examples in the file solids.c for
- some simple examples. Note the calls to m_xformpt() at the end for
- each vertex. This applies the current transformation matrix to the
- normalized data that the routine generates.
-
- MISSING STUFF
-
- NURBS surfaces, solids of extrusion, blending, mitering, ... Also, it
- would be nice to have some routines to generate triangle meshes from the
- input, etc.
-
-
-
-
-